package org.ly817.my.spring.core.ioc.factory;

import org.ly817.victorinox.basic.util.Assert;
import org.ly817.victorinox.basic.util.ObjectUtils;
import org.ly817.my.spring.core.ioc.bean.BeanDefinition;
import org.ly817.my.spring.core.ioc.bean.PropertyValue;
import org.ly817.my.spring.core.ioc.bean.enhance.BeanPostProcessor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * bean配置信息加载并生成到bean容器中
 *
 * Created by LuoYu on 2018/4/7.
 */
public abstract class AbstractBeanFactory implements BeanFactory {

    /**
     * bean容器
     * 使用ConcurrentHashMap线程安全map实现
     * 用于bean元信息的注册、查询、取用
     */
    private Map<String, BeanDefinition> beanContainer = new ConcurrentHashMap<String, BeanDefinition>();

    /**
     * 容器注册bean名册
     * 用于factory内部遍历容器
     *
     * @see AbstractBeanFactory getBeansByType(Class type)
     */
    private final List<String> beanDefinitionNames = new ArrayList<String>();

    /**
     * bean初始化完成后，从容器中根据是否实现BeanPostProcessor接口
     * 将对应的bean装饰器，实例化并注册到beanPostProcessors中
     * 在其他bean实例化时，就会调用beanPostProcessors中所有的装饰器对bean进行处理
     * 执行逻辑在context的registerBeanPostProcessors方法
     *
     * @see org.ly817.my.spring.core.context.AbstractApplicationContext
     * registBeanPostProcessors(AbstractBeanFactory abstractBeanFactory)
     */
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<BeanPostProcessor>();

    /**
     * 从容器中获取bean
     * 供外界从容器中获取bean
     *
     * @param name
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String name) throws Exception {
        BeanDefinition beanDefinition = beanContainer.get(name);
        Assert.isNotNull(beanDefinition, "容器中没有名为：" + name + "的bean！");
        Object bean = beanDefinition.getBean();
        // 初始调用，进行实例化-初始化
//        if (!ObjectUtils.isEmpty(bean)){
        if (bean == null) {
            bean = doCreateBean(beanDefinition);
            bean = initializeBean(bean,name);
        }

        // 检查bean的属性列表中是否有bean依赖
        return bean;
    }

    /**
     * 将bean元信息注册到容器中
     * 由上下文执行
     *
     * @param name
     * @param beanDefinition
     *
     */
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws Exception {
//        Object bean = doCreateBean(beanDefinition);
//        beanDefinition.setBean(bean);
        beanContainer.put(name, beanDefinition);
        beanDefinitionNames.add(name);
    }

    /**
     * 自动初始化容器中所有bean
     *
     * @throws Exception
     */
    public void preInstantiateSingletons() throws Exception {
        for (Iterator it = this.beanDefinitionNames.iterator(); it.hasNext();) {
            String beanName = (String) it.next();
            getBean(beanName);
        }
    }

    /**
     * 根据bean信息创建实例
     * - 反射创建实例
     * - 将实例放入容器
     * - 注入bean属性
     *
     * @param beanDefinition
     * @return
     */
    protected Object doCreateBean(BeanDefinition beanDefinition) throws Exception {
        Object bean = createBeanInstance(beanDefinition);
        beanDefinition.setBean(bean);
        applyPropertyValues(bean, beanDefinition);
        return bean;
    }

    /**
     * 利用反射实例化bean对象
     *
     * @param beanDefinition
     * @return
     * @throws Exception
     */
    protected Object createBeanInstance(BeanDefinition beanDefinition) throws Exception {
        return beanDefinition.getBeanClass().newInstance();
    }

    /**
     * 将bean元信息中的属性注入bean实例中
     *
     * @param bean
     * @param beanDefinition
     * @throws Exception
     */
    protected abstract void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception ;

    /**
     * 首次调用时，实例化容器中注册的bean
     */
    protected Object initializeBean(Object bean, String name) throws Exception {
        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessBeforeInitialization(bean, name);
        }

        for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
            bean = beanPostProcessor.postProcessAfterInitialization(bean, name);
        }
        return bean;
    }


    /**
     * 注册bean装饰器
     *
     */
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        // 空指针检查
//        Assert.isNull(beanPostProcessor, "bean装饰器（" + beanPostProcessor.getClass().getName() + "）实例化失败！");
        this.beanPostProcessors.remove(beanPostProcessor);
        this.beanPostProcessors.add(beanPostProcessor);
    }



    public List getBeansByType(Class type) throws Exception {
        List beans = new ArrayList<Object>();
        for (String beanDefinitionName : beanDefinitionNames) {
            if (type.isAssignableFrom(beanContainer.get(beanDefinitionName).getBeanClass())) {
                beans.add(getBean(beanDefinitionName));
            }
        }
        return beans;
    }


}
